package com.ethink.warehouse.service.impl;

import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ethink.component.warehouse.WarehouseClient;
import com.ethink.component.warehouse.WarehouseInter;
import com.ethink.component.warehouse.properties.ExternalApiEtonbaoProperties;
import com.ethink.framework.common.constant.CommonRespCode;
import com.ethink.framework.common.db.PageUtils;
import com.ethink.framework.common.exception.BusinessException;
import com.ethink.id.generator.IdGenerator;
import com.ethink.warehouse.api.component.enums.WarehouseInStockTypeEnum;
import com.ethink.warehouse.api.component.enums.WarehouseInventoryTypeEnum;
import com.ethink.warehouse.api.component.enums.WarehouseOutStockTypeEnum;
import com.ethink.warehouse.api.component.info.WarehouseInStockItemInfo;
import com.ethink.warehouse.api.component.info.WarehouseOutStockItemInfo;
import com.ethink.warehouse.api.component.request.*;
import com.ethink.warehouse.api.component.response.WarehouseAddInStockResponse;
import com.ethink.warehouse.api.component.response.WarehouseAddOutStockResponse;
import com.ethink.warehouse.api.component.response.WarehouseCancelInStockResponse;
import com.ethink.warehouse.api.component.response.WarehouseCancelOutStockResponse;
import com.ethink.warehouse.api.constant.WarehouseRespCode;
import com.ethink.warehouse.api.enums.*;
import com.ethink.warehouse.api.info.*;
import com.ethink.warehouse.api.request.*;
import com.ethink.warehouse.api.response.*;
import com.ethink.warehouse.common.entity.*;
import com.ethink.warehouse.common.mapper.*;
import com.ethink.warehouse.common.model.MateInventoryAutoAdjustItemModel;
import com.ethink.warehouse.common.model.MateInventoryAutoAdjustModel;
import com.ethink.warehouse.manager.LogisticsBizConfigManager;
import com.ethink.warehouse.manager.LogisticsStepManager;
import com.ethink.warehouse.manager.MateInventoryAdjustManager;
import com.ethink.warehouse.manager.MateInventoryManager;
import com.ethink.warehouse.service.LogisticsTraceOrderService;
import com.ethink.warehouse.service.StockInOrderService;
import com.ethink.warehouse.service.StockOutOrderService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 物流跟进单 Service接口实现
 *
 * @author default
 * @date 2022-11-10
 */
@Slf4j
@Service
public class LogisticsTraceOrderServiceImpl implements LogisticsTraceOrderService {

    @Autowired
    private LogisticsTraceOrderMapper logisticsTraceOrderMapper;

    @Autowired
    private LogisticsTraceApplyOrderMapper logisticsTraceApplyOrderMapper;

    @Autowired
    private LogisticsTraceApplyDetailOrderMapper logisticsTraceApplyDetailOrderMapper;

    @Autowired
    private LogisticsTraceDetailOrderMapper logisticsTraceDetailOrderMapper;

    @Autowired
    private SubWarehouseMapper subWarehouseMapper;

    @Autowired
    private WarehouseBookMapper warehouseBookMapper;

    @Autowired
    private StockInOrderMapper stockInOrderMapper;

    @Autowired
    private StockInOrderDetailMapper stockInOrderDetailMapper;

    @Autowired
    private StockInOrderDetailBatchMapper stockInOrderDetailBatchMapper;

    @Autowired
    private StockOutOrderMapper stockOutOrderMapper;

    @Autowired
    private StockOutOrderDetailMapper stockOutOrderDetailMapper;

    @Autowired
    private StockOutOrderDetailBatchMapper stockOutOrderDetailBatchMapper;

    @Autowired
    private MateInventoryMapper mateInventoryMapper;

    @Autowired
    private StockInOrderService stockInOrderService;

    @Autowired
    private StockOutOrderService stockOutOrderService;

    @Autowired
    private LogisticsStepManager logisticsStepManager;

    @Autowired
    private MateInventoryManager mateInventoryManager;

    @Autowired
    private ExternalApiEtonbaoProperties externalApiEtonbaoProperties;

    @Autowired
    private WarehouseClient warehouseClient;

    @Autowired
    private LogisticsBizConfigManager logisticsBizConfigManager;

    @Autowired
    private IdGenerator idGenerator;

    @Autowired
    private MateInventoryAdjustManager mateInventoryAdjustManager;

    @Override
    public Page<LogisticsTraceOrderQueryResponse> queryLogisticsTraceOrder(LogisticsTraceOrderQueryRequest request) {
        return logisticsTraceOrderMapper.queryLogisticsTraceOrder(PageUtils.buildPage(request), request);
    }

    @Override
    public Page<LogisticsTraceOrderQueryGoodsModeResponse> queryLogisticsTraceOrderGoodsMode(LogisticsTraceOrderQueryGoodsModeRequest request) {
        return logisticsTraceOrderMapper.queryLogisticsTraceOrderGoodsMode(PageUtils.buildPage(request), request);
    }

    @Override
    public LogisticsTraceOrderGetDetailResponse getLogisticsTraceOrderById(Long logisticsTraceOrderId) {
        LogisticsTraceOrderGetDetailResponse response = new LogisticsTraceOrderGetDetailResponse();
        LogisticsTraceOrderInfo logisticsTraceOrderInfo = logisticsTraceOrderMapper.getLogisticsTraceOrderById(logisticsTraceOrderId);
        if (logisticsTraceOrderInfo == null) {
            return null;
        }
        // 物流跟进单详情
        response.setLogisticsTraceOrderInfo(logisticsTraceOrderInfo);
        // 流程节点列表
        String[] stepCodes = logisticsTraceOrderInfo.getStepType().split(",");
        List<LogisticsStepInfo> logisticsStepInfoList = new ArrayList<>();
        for (String stepCode : stepCodes) {
            LogisticsTraceStepEnum logisticsTraceStepEnum = LogisticsTraceStepEnum.valueOfCode(stepCode);
            if (logisticsTraceStepEnum != null) {
                LogisticsStepInfo logisticsStepInfo = new LogisticsStepInfo();
                logisticsStepInfo.setCode(stepCode);
                logisticsStepInfo.setName(logisticsTraceStepEnum.getName());
                logisticsStepInfo.setIsSelected(logisticsTraceOrderInfo.getStepStatus().equals(stepCode));
                logisticsStepInfoList.add(logisticsStepInfo);
            }
        }
        response.setLogisticsStepInfoList(logisticsStepInfoList);
        // 申请单列表
        response.setLogisticsTraceApplyDetailOrderList(logisticsTraceApplyDetailOrderMapper.listLogisticsTraceApplyDetailOrderByTraceOrderNo(logisticsTraceOrderInfo.getOrderNo()));
        // 跟进明细列表
        response.setLogisticsTraceDetailOrderInfoList(logisticsTraceDetailOrderMapper.listLogisticsTraceDetailOrderByTraceOrderNo(logisticsTraceOrderInfo.getOrderNo(), null));
        // 外部仓库入库详情列表
        response.setStockInOrderDetailInfoList(stockInOrderDetailMapper.listStockInOrderDetailByOrderNo(logisticsTraceOrderInfo.getOrderNo()));
        // 外部仓库入库批次详情列表
        response.setStockInOrderDetailBatchInfoList(stockInOrderDetailBatchMapper.listStockInOrderDetailBatchByOrderNo(logisticsTraceOrderInfo.getOrderNo()));
        // 外部仓库出库详情列表
        response.setStockOutOrderDetailInfoList(stockOutOrderDetailMapper.listStockOutOrderDetailByOrderNo(logisticsTraceOrderInfo.getOrderNo()));
        // 外部仓库出库批次详情列表
        response.setStockOutOrderDetailBatchInfoList(stockOutOrderDetailBatchMapper.listStockOutOrderDetailBatchByOrderNo(logisticsTraceOrderInfo.getOrderNo()));
        // 是否需要提交确认明细
        response.setNeedConfirm(logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.H.getCode()));
        // 是否可以返回上一步
        response.setCanBackPreviousStep(
                !logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.A.getCode()) &&
                        !logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.H2.getCode()) &&
                        !logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.I.getCode())
        );
        // 是否可以提交下一步
        LogisticsBizConfigInfo logisticsBizConfigInfo = logisticsBizConfigManager.getLogisticsBizConfigInfo(logisticsTraceOrderInfo.getBizConfig());
        response.setCanSubmitNextStep(
                !logisticsTraceOrderInfo.getOrderStatus().equals(LogisticsTraceOrderOrderStatusEnum.S3.getIntValue()) && (
                        !logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.B.getCode()) || !logisticsBizConfigInfo.isWbckApplied()
                )
        );
        return response;
    }

    @Override
    public List<LogisticsTraceOrderListRkConfirmDetailResponse> listLogisticsTraceOrderRkConfirmDetail(Long logisticsTraceOrderId) {
        LogisticsTraceOrderInfo logisticsTraceOrderInfo = logisticsTraceOrderMapper.getLogisticsTraceOrderById(logisticsTraceOrderId);
        if (logisticsTraceOrderInfo.getOrderStatus().equals(LogisticsTraceOrderOrderStatusEnum.S3.getIntValue())) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_UPDATE_STATUS_ERR);
        }

        List<LogisticsTraceOrderListRkConfirmDetailResponse> response = new ArrayList<>();
        if (logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T11.getCode())) {
            // 采购入库
            List<LogisticsTraceDetailOrderGetDetailResponse> traceDetailList =
                    logisticsTraceDetailOrderMapper.listLogisticsTraceDetailOrderByTraceOrderNo(logisticsTraceOrderInfo.getOrderNo(), LogisticsTraceDetailOrderTypeEnum.T1.getIntValue());
            if (CollectionUtils.isNotEmpty(traceDetailList)) {
                response.addAll(traceDetailList.stream().map(item -> {
                    LogisticsTraceOrderListRkConfirmDetailResponse confirm = new LogisticsTraceOrderListRkConfirmDetailResponse();
                    BeanUtils.copyProperties(item, confirm);
                    return confirm;
                }).collect(Collectors.toList()));
            } else {
                List<LogisticsTraceApplyDetailOrderGetDetailResponse> applyList = logisticsTraceApplyDetailOrderMapper.listLogisticsTraceApplyDetailOrderByTraceOrderNo(logisticsTraceOrderInfo.getOrderNo());
                response.addAll(applyList.stream().map(item -> {
                    LogisticsTraceOrderListRkConfirmDetailResponse confirm = new LogisticsTraceOrderListRkConfirmDetailResponse();
                    BeanUtils.copyProperties(item, confirm);
                    confirm.setSubWarehouseCode(item.getSourceSubWarehouseCode());
                    confirm.setSubWarehouseName(item.getSourceSubWarehouseName());
                    return confirm;
                }).collect(Collectors.toList()));
            }
        } else if (logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T32.getCode())) {
            // 物流调拨入库
            List<LogisticsTraceDetailOrderGetDetailResponse> traceDetailList =
                    logisticsTraceDetailOrderMapper.listLogisticsTraceDetailOrderByTraceOrderNo(logisticsTraceOrderInfo.getOrderNo(), null);
            List<LogisticsTraceDetailOrderGetDetailResponse> rkList =
                    traceDetailList.stream().filter(item -> LogisticsTraceDetailOrderTypeEnum.T1.eq(item.getOrderType()))
                            .collect(Collectors.toList());
            List<LogisticsTraceDetailOrderGetDetailResponse> ckList =
                    traceDetailList.stream().filter(item -> LogisticsTraceDetailOrderTypeEnum.T2.eq(item.getOrderType()))
                            .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(rkList)) {
                response.addAll(rkList.stream().map(item -> {
                    LogisticsTraceOrderListRkConfirmDetailResponse confirm = new LogisticsTraceOrderListRkConfirmDetailResponse();
                    BeanUtils.copyProperties(item, confirm);
                    return confirm;
                }).collect(Collectors.toList()));
            } else {
                response.addAll(ckList.stream().map(item -> {
                    LogisticsTraceOrderListRkConfirmDetailResponse confirm = new LogisticsTraceOrderListRkConfirmDetailResponse();
                    BeanUtils.copyProperties(item, confirm);

                    LogisticsTraceApplyOrder applyOrder = logisticsTraceApplyOrderMapper.selectOne(
                            new LambdaQueryWrapper<LogisticsTraceApplyOrder>()
                                    .eq(LogisticsTraceApplyOrder::getApplyOrderNo, item.getApplyOrderNo())
                    );
                    confirm.setSubWarehouseCode(applyOrder.getTargetSubWarehouseCode());
                    confirm.setSubWarehouseName(applyOrder.getTargetSubWarehouseName());
                    confirm.setOrderType(LogisticsTraceDetailOrderTypeEnum.T2.getIntValue());
                    confirm.setMateInventoryId(null);
                    confirm.setApplyInQty(item.getActualInQty());
                    confirm.setTraceQty(item.getActualInQty());
                    confirm.setActualInQty(item.getActualInQty());
                    return confirm;
                }).collect(Collectors.toList()));
            }
        } else if (logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T41.getCode()) || logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T71.getCode())) {
            // 预售退货/销售退货
            List<LogisticsTraceApplyDetailOrderGetDetailResponse> applyList = logisticsTraceApplyDetailOrderMapper.listLogisticsTraceApplyDetailOrderByTraceOrderNo(logisticsTraceOrderInfo.getOrderNo());
            response.addAll(applyList.stream().map(item -> {
                LogisticsTraceOrderListRkConfirmDetailResponse confirm = new LogisticsTraceOrderListRkConfirmDetailResponse();
                BeanUtils.copyProperties(item, confirm);
                confirm.setSubWarehouseCode(item.getSourceSubWarehouseCode());
                confirm.setSubWarehouseName(item.getSourceSubWarehouseName());
                return confirm;
            }).collect(Collectors.toList()));
        }

        for (LogisticsTraceOrderListRkConfirmDetailResponse item : response) {
            item.setId(null);
            item.setIsDeleted(null);
            item.setCreateTime(null);
            item.setCreateUser(null);
            item.setUpdateTime(null);
            item.setUpdateUser(null);
            item.setRemark(null);
        }
        return response;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LogisticsTraceOrderAddResponse addLogisticsTraceOrder(LogisticsTraceOrderAddRequest request) {
        if (CollectionUtils.isEmpty(request.getItems())) {
            throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细不能为空！");
        }

        LogisticsTraceOrderAddResponse response = new LogisticsTraceOrderAddResponse();
        LogisticsTraceOrder logisticsTraceOrder = new LogisticsTraceOrder();
        BeanUtils.copyProperties(request, logisticsTraceOrder);

        String orderNo = idGenerator.ts();
        logisticsTraceOrder.setOrderNo(orderNo);
        logisticsTraceOrder.setOrderStatus(LogisticsTraceOrderOrderStatusEnum.S1.getIntValue());
        logisticsTraceOrder.setStepStatus(LogisticsTraceStepEnum.A.getCode());

        LogisticsStepListGetRequest logisticsStepListGetRequest = new LogisticsStepListGetRequest();
        logisticsStepListGetRequest.setSourceEntityType(request.getSourceEntityType());
        logisticsStepListGetRequest.setSourceWarehouseCode(request.getSourceWarehouseCode());
        logisticsStepListGetRequest.setTargetEntityType(request.getTargetEntityType());
        logisticsStepListGetRequest.setTargetWarehouseCode(request.getTargetWarehouseCode());
        logisticsStepListGetRequest.setTransportEntity(request.getTransportEntity());
        logisticsTraceOrder.setStepType(logisticsStepManager.getLogisticsStepString(logisticsStepListGetRequest));

        // 商品明细
        String checkingRealWarehouseBookGroup = null;
        String checkingSourceOrderType = null;
        String checkingSourceSubWarehouseCode = null;
        String checkingTargetSubWarehouseCode = null;
        int i = 0;
        for (LogisticsTraceOrderAddRequest.LogisticsTraceOrderAddRequestMateItem item : request.getItems()) {
            LogisticsTraceApplyOrder traceApplyOrder = logisticsTraceApplyOrderMapper.selectOne(
                    new LambdaQueryWrapper<LogisticsTraceApplyOrder>()
                            .eq(LogisticsTraceApplyOrder::getId, item.getId())
                            .eq(LogisticsTraceApplyOrder::getIsDeleted, false)
            );
            if (traceApplyOrder == null) {
                throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细申请单主键错误！");
            }

            List<LogisticsTraceApplyDetailOrder> tracingApplyDetailOrders = logisticsTraceApplyDetailOrderMapper.selectList(
                    new LambdaQueryWrapper<LogisticsTraceApplyDetailOrder>()
                            .eq(LogisticsTraceApplyDetailOrder::getApplyOrderNo, traceApplyOrder.getApplyOrderNo())
                            .eq(LogisticsTraceApplyDetailOrder::getIsDeleted, false)
            );
            Integer tracingSum = tracingApplyDetailOrders.stream().mapToInt(LogisticsTraceApplyDetailOrder::getTraceQty).sum();
            if (item.getTraceQty() > traceApplyOrder.getApplyInQty() - tracingSum || item.getTraceQty() <= 0) {
                throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细实际申请数量不合法！");
            }

            if (i == 0) {
                checkingSourceOrderType = traceApplyOrder.getSourceOrderType();
                checkingSourceSubWarehouseCode = traceApplyOrder.getSourceSubWarehouseCode();
                checkingTargetSubWarehouseCode = traceApplyOrder.getTargetSubWarehouseCode();
            }
            if (!StrUtil.equals(traceApplyOrder.getSourceOrderType(), checkingSourceOrderType)) {
                throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细原始订单类型不一致！");
            }
            if (!StrUtil.equals(traceApplyOrder.getSourceSubWarehouseCode(), checkingSourceSubWarehouseCode) || !StrUtil.equals(traceApplyOrder.getTargetSubWarehouseCode(), checkingTargetSubWarehouseCode)) {
                throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细分仓不一致！");
            }

            // 商品申请单明细
            LogisticsTraceApplyDetailOrder traceApplyDetailOrder = new LogisticsTraceApplyDetailOrder();
            traceApplyDetailOrder.setApplyOrderNo(traceApplyOrder.getApplyOrderNo());
            traceApplyDetailOrder.setTraceOrderNo(orderNo);
            traceApplyDetailOrder.setTraceQty(item.getTraceQty());
            if (StrUtil.equals(checkingSourceOrderType, SubjectEnum.T11.getCode())) {
                // 采购入库，报关账册
                SubWarehouseInfo bkSubWarehouseInfo = subWarehouseMapper.getSubWarehouseByCode(item.getBkSubWarehouseCode());
                if (bkSubWarehouseInfo.getWarehouseBookId() == null) {
                    throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细报关账册未维护！");
                }
                WarehouseBookInfo realWarehouseBookInfo = warehouseBookMapper.getWarehouseBookById(bkSubWarehouseInfo.getWarehouseBookId());
                if (i == 0) {
                    checkingRealWarehouseBookGroup = realWarehouseBookInfo.getBookGroup();
                }
                if (!StrUtil.equals(realWarehouseBookInfo.getBookGroup(), checkingRealWarehouseBookGroup)) {
                    throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细报关账册不属于同一分组！");
                }
                traceApplyDetailOrder.setBkSubWarehouseCode(bkSubWarehouseInfo.getSubWarehouseCode());
                traceApplyDetailOrder.setBkSubWarehouseName(bkSubWarehouseInfo.getSubWarehouseName());
            }
            logisticsTraceApplyDetailOrderMapper.insert(traceApplyDetailOrder);
            i++;
        }

        logisticsTraceOrder.setBizConfig(logisticsBizConfigManager.getLogisticsBizConfigString(new LogisticsBizConfigInfo()));
        logisticsTraceOrder.setSourceOrderType(checkingSourceOrderType);

        logisticsTraceOrderMapper.insert(logisticsTraceOrder);

        response.setId(logisticsTraceOrder.getId());
        return response;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateLogisticsTraceOrder(LogisticsTraceOrderUpdateRequest request) {
        LogisticsTraceOrderInfo logisticsTraceOrderInfo = logisticsTraceOrderMapper.getLogisticsTraceOrderById(request.getId());
        if (!logisticsTraceOrderInfo.getOrderStatus().equals(LogisticsTraceOrderOrderStatusEnum.S1.getIntValue())) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_UPDATE_STATUS_ERR);
        }

        LambdaUpdateWrapper<LogisticsTraceOrder> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(LogisticsTraceOrder::getId, request.getId());
        updateWrapper.set(LogisticsTraceOrder::getSourceWarehouseCode, request.getSourceWarehouseCode());
        updateWrapper.set(LogisticsTraceOrder::getSourceWarehouseName, request.getSourceWarehouseName());
        updateWrapper.set(LogisticsTraceOrder::getTargetWarehouseCode, request.getTargetWarehouseCode());
        updateWrapper.set(LogisticsTraceOrder::getTargetWarehouseName, request.getTargetWarehouseName());
        updateWrapper.set(LogisticsTraceOrder::getTransportEntity, request.getTransportEntity());
        updateWrapper.set(LogisticsTraceOrder::getTargetEntityType, request.getTargetEntityType());
        updateWrapper.set(LogisticsTraceOrder::getSourceEntityType, request.getSourceEntityType());
        updateWrapper.set(LogisticsTraceOrder::getTakeOrderNo, request.getTakeOrderNo());
        updateWrapper.set(LogisticsTraceOrder::getSourceCountry, request.getSourceCountry());
        updateWrapper.set(LogisticsTraceOrder::getSourcePort, request.getSourcePort());
        updateWrapper.set(LogisticsTraceOrder::getTargetCountry, request.getTargetCountry());
        updateWrapper.set(LogisticsTraceOrder::getTargetPort, request.getTargetPort());
        updateWrapper.set(LogisticsTraceOrder::getExpressCompany, request.getExpressCompany());
        updateWrapper.set(LogisticsTraceOrder::getTransportModel, request.getTransportModel());
        updateWrapper.set(LogisticsTraceOrder::getDeliveryModel, request.getDeliveryModel());
        updateWrapper.set(LogisticsTraceOrder::getTorr, request.getTorr());
        updateWrapper.set(LogisticsTraceOrder::getBox, request.getBox());
        updateWrapper.set(LogisticsTraceOrder::getExpireFhTime, request.getExpireFhTime());
        updateWrapper.set(LogisticsTraceOrder::getActualFhTime, request.getActualFhTime());
        updateWrapper.set(LogisticsTraceOrder::getExpireDgTime, request.getExpireDgTime());
        updateWrapper.set(LogisticsTraceOrder::getActualDgTime, request.getActualDgTime());
        updateWrapper.set(LogisticsTraceOrder::getExpireDcTime, request.getExpireDcTime());
        updateWrapper.set(LogisticsTraceOrder::getActualDcTime, request.getActualDcTime());
        updateWrapper.set(LogisticsTraceOrder::getActualLhTime, request.getActualLhTime());
        updateWrapper.set(LogisticsTraceOrder::getActualSjTime, request.getActualSjTime());
        updateWrapper.set(LogisticsTraceOrder::getTransportCostAmt, request.getTransportCostAmt());
        updateWrapper.set(LogisticsTraceOrder::getRemark, request.getRemark());
        logisticsTraceOrderMapper.update(null, updateWrapper);

        // 商品明细
        // 删除原有的申请单明细
        LambdaQueryWrapper<LogisticsTraceApplyDetailOrder> deleteApplyDetailOrderWrapper = new LambdaQueryWrapper<>();
        deleteApplyDetailOrderWrapper.eq(LogisticsTraceApplyDetailOrder::getTraceOrderNo, logisticsTraceOrderInfo.getOrderNo());
        logisticsTraceApplyDetailOrderMapper.delete(deleteApplyDetailOrderWrapper);
        // 申请单明细新增
        String checkingRealWarehouseBookGroup = null;
        String checkingSourceOrderType = null;
        int i = 0;
        for (LogisticsTraceOrderUpdateRequest.LogisticsTraceOrderUpdateRequestMateItem item : request.getItems()) {
            LogisticsTraceApplyOrder traceApplyOrder = logisticsTraceApplyOrderMapper.selectOne(
                    new LambdaQueryWrapper<LogisticsTraceApplyOrder>()
                            .eq(LogisticsTraceApplyOrder::getId, item.getId())
                            .eq(LogisticsTraceApplyOrder::getIsDeleted, false)
            );
            if (traceApplyOrder == null) {
                throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细申请单主键错误！");
            }

            List<LogisticsTraceApplyDetailOrder> tracingApplyDetailOrders = logisticsTraceApplyDetailOrderMapper.selectList(
                    new LambdaQueryWrapper<LogisticsTraceApplyDetailOrder>()
                            .eq(LogisticsTraceApplyDetailOrder::getApplyOrderNo, traceApplyOrder.getApplyOrderNo())
                            .eq(LogisticsTraceApplyDetailOrder::getIsDeleted, false)
            );
            Integer tracingSum = tracingApplyDetailOrders.stream().mapToInt(LogisticsTraceApplyDetailOrder::getTraceQty).sum();
            if (item.getTraceQty() > traceApplyOrder.getApplyInQty() - tracingSum || item.getTraceQty() <= 0) {
                throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细实际申请数量不合法！");
            }

            if (i == 0) {
                checkingSourceOrderType = traceApplyOrder.getSourceOrderType();
            }
            if (!traceApplyOrder.getSourceOrderType().equals(checkingSourceOrderType)) {
                throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细原始订单类型不一致！");
            }

            // TODO: 除了采购入库，选中的入库/出库申请记录 对应的每一个原单号和原明细单号必须完整

            // 商品申请单明细
            LogisticsTraceApplyDetailOrder traceApplyDetailOrder = new LogisticsTraceApplyDetailOrder();
            traceApplyDetailOrder.setApplyOrderNo(traceApplyOrder.getApplyOrderNo());
            traceApplyDetailOrder.setTraceOrderNo(logisticsTraceOrderInfo.getOrderNo());
            traceApplyDetailOrder.setTraceQty(item.getTraceQty());
            if (checkingSourceOrderType.equals(SubjectEnum.T11.getCode())) {
                // 采购入库，报关账册
                SubWarehouseInfo bkSubWarehouseInfo = subWarehouseMapper.getSubWarehouseByCode(item.getBkSubWarehouseCode());
                if (bkSubWarehouseInfo.getWarehouseBookId() == null) {
                    throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细报关账册未维护！");
                }
                WarehouseBookInfo realWarehouseBookInfo = warehouseBookMapper.getWarehouseBookById(bkSubWarehouseInfo.getWarehouseBookId());
                if (i == 0) {
                    checkingRealWarehouseBookGroup = realWarehouseBookInfo.getBookGroup();
                }
                if (!realWarehouseBookInfo.getBookGroup().equals(checkingRealWarehouseBookGroup)) {
                    throw new BusinessException(CommonRespCode.PARAM_ERR, "商品明细报关账册不属于同一分组！");
                }
                traceApplyDetailOrder.setBkSubWarehouseCode(bkSubWarehouseInfo.getSubWarehouseCode());
                traceApplyDetailOrder.setBkSubWarehouseName(bkSubWarehouseInfo.getSubWarehouseName());
            }
            logisticsTraceApplyDetailOrderMapper.insert(traceApplyDetailOrder);
            i++;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LogisticsTraceOrderUpdateStepResponse updateLogisticsTraceOrderStep(LogisticsTraceOrderUpdateStepRequest request) {
        LogisticsTraceOrderInfo logisticsTraceOrderInfo = logisticsTraceOrderMapper.getLogisticsTraceOrderById(request.getId());
        LogisticsBizConfigInfo logisticsBizConfigInfo = logisticsBizConfigManager.getLogisticsBizConfigInfo(logisticsTraceOrderInfo.getBizConfig());
        if (
                logisticsTraceOrderInfo.getOrderStatus().equals(LogisticsTraceOrderOrderStatusEnum.S3.getIntValue()) || (
                        logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.B.getCode()) &&
                                logisticsBizConfigInfo.isWbckApplied()
                )
        ) {
            // 请求过外部仓库接口时，不允许提交下一步（B -> C）
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_UPDATE_STEP_STATUS_ERR);
        }
        LogisticsTraceOrderUpdateStepResponse response = new LogisticsTraceOrderUpdateStepResponse();

        LogisticsTraceOrder logisticsTraceOrder = new LogisticsTraceOrder();
        BeanUtils.copyProperties(logisticsTraceOrderInfo, logisticsTraceOrder);

        List<LogisticsTraceApplyOrder> logisticsTraceApplyOrderList = new ArrayList<>();
        logisticsTraceApplyOrderMapper.listLogisticsTraceApplyOrderByTraceOrderNo(logisticsTraceOrderInfo.getOrderNo()).forEach(item -> {
            LogisticsTraceApplyOrder logisticsTraceApplyOrder = new LogisticsTraceApplyOrder();
            BeanUtils.copyProperties(item, logisticsTraceApplyOrder);
            logisticsTraceApplyOrderList.add(logisticsTraceApplyOrder);
        });

        List<LogisticsTraceApplyDetailOrder> logisticsTraceApplyDetailOrderList = logisticsTraceApplyDetailOrderMapper.selectList(
                new LambdaQueryWrapper<LogisticsTraceApplyDetailOrder>()
                        .eq(LogisticsTraceApplyDetailOrder::getTraceOrderNo, logisticsTraceOrderInfo.getOrderNo())
                        .eq(LogisticsTraceApplyDetailOrder::getIsDeleted, false));
        LambdaUpdateWrapper<LogisticsTraceOrder> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(LogisticsTraceOrder::getId, request.getId());
        updateWrapper.set(LogisticsTraceOrder::getTakeOrderNo, request.getTakeOrderNo());
        updateWrapper.set(LogisticsTraceOrder::getSourceCountry, request.getSourceCountry());
        updateWrapper.set(LogisticsTraceOrder::getSourcePort, request.getSourcePort());
        updateWrapper.set(LogisticsTraceOrder::getTargetCountry, request.getTargetCountry());
        updateWrapper.set(LogisticsTraceOrder::getTargetPort, request.getTargetPort());
        updateWrapper.set(LogisticsTraceOrder::getExpressCompany, request.getExpressCompany());
        updateWrapper.set(LogisticsTraceOrder::getTransportModel, request.getTransportModel());
        updateWrapper.set(LogisticsTraceOrder::getDeliveryModel, request.getDeliveryModel());
        updateWrapper.set(LogisticsTraceOrder::getTorr, request.getTorr());
        updateWrapper.set(LogisticsTraceOrder::getBox, request.getBox());
        updateWrapper.set(LogisticsTraceOrder::getExpireFhTime, request.getExpireFhTime());
        updateWrapper.set(LogisticsTraceOrder::getActualFhTime, request.getActualFhTime());
        updateWrapper.set(LogisticsTraceOrder::getExpireDgTime, request.getExpireDgTime());
        updateWrapper.set(LogisticsTraceOrder::getActualDgTime, request.getActualDgTime());
        updateWrapper.set(LogisticsTraceOrder::getExpireDcTime, request.getExpireDcTime());
        updateWrapper.set(LogisticsTraceOrder::getActualDcTime, request.getActualDcTime());
        updateWrapper.set(LogisticsTraceOrder::getActualLhTime, request.getActualLhTime());
        updateWrapper.set(LogisticsTraceOrder::getActualSjTime, request.getActualSjTime());
        updateWrapper.set(LogisticsTraceOrder::getTransportCostAmt, request.getTransportCostAmt());
        updateWrapper.set(LogisticsTraceOrder::getPackageFileId, request.getPackageFileId());
        updateWrapper.set(LogisticsTraceOrder::getConfirmFileId, request.getConfirmFileId());
        updateWrapper.set(LogisticsTraceOrder::getRemark, request.getRemark());
        if (logisticsTraceOrderInfo.getOrderStatus().equals(LogisticsTraceOrderOrderStatusEnum.S1.getIntValue())) {
            updateWrapper.set(LogisticsTraceOrder::getOrderStatus, LogisticsTraceOrderOrderStatusEnum.S2.getIntValue());
        }
        // 流程状态
        String[] stepCodes = logisticsTraceOrderInfo.getStepType().split(",");
        int currentStepIndex = Arrays.binarySearch(stepCodes, logisticsTraceOrderInfo.getStepStatus());
        String nextStepStatus = stepCodes[currentStepIndex + 1];
        updateWrapper.set(LogisticsTraceOrder::getStepStatus, nextStepStatus);

        if (logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.C.getCode())) {
            // 出库逻辑
            this.outFinishing(
                    logisticsTraceOrder,
                    logisticsTraceApplyOrderList,
                    logisticsTraceApplyDetailOrderList,
                    logisticsBizConfigInfo
            );
        } else if (logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.H.getCode())) {
            // 单据确认
            if (CollectionUtils.isEmpty(request.getConfirmItems())) {
                throw new BusinessException(CommonRespCode.PARAM_ERR, "确认明细不能为空！");
            }

            List<LogisticsTraceOrderListRkConfirmDetailResponse> rkConfirmDetailList = this.listLogisticsTraceOrderRkConfirmDetail(logisticsTraceOrderInfo.getId());
            Map<String, IntSummaryStatistics> rkConfirmDetailSummaryStatisticsMap = rkConfirmDetailList.stream().collect(
                    Collectors.groupingBy(item -> item.getApplyDetailId() + "-" + item.getMateSku(), Collectors.summarizingInt(LogisticsTraceOrderListRkConfirmDetailResponse::getTraceQty))
            );

            Map<String, IntSummaryStatistics> confirmItemsSummaryStatisticsMap = request.getConfirmItems().stream().collect(
                    Collectors.groupingBy(item -> item.getApplyDetailId() + "-" + item.getMateSku(), Collectors.summarizingInt(LogisticsTraceOrderUpdateStepRequest.LogisticsTraceOrderUpdateStepRequestMateItem::getActualInQty))
            );
            confirmItemsSummaryStatisticsMap.forEach((key, value) -> {
                IntSummaryStatistics rkConfirmDetailSummaryStatistics = rkConfirmDetailSummaryStatisticsMap.get(key);
                if (rkConfirmDetailSummaryStatistics == null) {
                    throw new BusinessException(CommonRespCode.PARAM_ERR, "确认明细数据不合法！");
                }
                if (value.getSum() > rkConfirmDetailSummaryStatistics.getSum()) {
                    throw new BusinessException(CommonRespCode.PARAM_ERR, "确认明细中最终入库数量不能大于跟进数量！");
                }
            });

            // 逻辑删除原有入库明细
            LambdaUpdateWrapper<LogisticsTraceDetailOrder> traceDetailUpdateWrapper = new LambdaUpdateWrapper<>();
            traceDetailUpdateWrapper.eq(LogisticsTraceDetailOrder::getTraceOrderNo, logisticsTraceOrderInfo.getOrderNo());
            traceDetailUpdateWrapper.eq(LogisticsTraceDetailOrder::getOrderType, LogisticsTraceDetailOrderTypeEnum.T1.getIntValue());
            traceDetailUpdateWrapper.set(LogisticsTraceDetailOrder::getIsDeleted, true);
            logisticsTraceDetailOrderMapper.update(null, traceDetailUpdateWrapper);
            // 新增入库明细
            for (LogisticsTraceOrderUpdateStepRequest.LogisticsTraceOrderUpdateStepRequestMateItem confirmItem : request.getConfirmItems()) {
                LogisticsTraceDetailOrder logisticsTraceDetailOrder = new LogisticsTraceDetailOrder();
                // 非前端传入字段
                logisticsTraceDetailOrder.setOrderType(LogisticsTraceDetailOrderTypeEnum.T1.getIntValue());
                // 前端传入的字段，原样复制
                BeanUtils.copyProperties(confirmItem, logisticsTraceDetailOrder);
                logisticsTraceDetailOrderMapper.insert(logisticsTraceDetailOrder);
            }
            logisticsBizConfigInfo.setRkConfirmed(true);
        }

        if (nextStepStatus.equals(LogisticsTraceStepEnum.B.getCode())) {
            if (!logisticsBizConfigInfo.isWbckApplied()) {
                int countSucceed = this.externalWarehouseOutApply(logisticsTraceOrderInfo);
                logisticsBizConfigInfo.setWbckApplied(countSucceed > 0);
            }
        }
//        else if (nextStepStatus.equals(LogisticsTraceStepEnum.C.getCode())) {
//            // 出库逻辑
//            this.outFinishing(
//                    logisticsTraceOrder,
//                    logisticsTraceApplyOrderList,
//                    logisticsTraceApplyDetailOrderList,
//                    logisticsBizConfigInfo
//            );
//        }
        else if (nextStepStatus.equals(LogisticsTraceStepEnum.F.getCode())) {
            if (!logisticsBizConfigInfo.isWbrkApplied()) {
                int countSucceed = this.externalWarehouseInApply(logisticsTraceOrderInfo);
                logisticsBizConfigInfo.setWbrkApplied(countSucceed > 0);
            }
        } else if (nextStepStatus.equals(LogisticsTraceStepEnum.I.getCode())) {
            // 入库逻辑
            this.inFinishing(
                    logisticsTraceOrder,
                    logisticsTraceApplyOrderList,
                    logisticsTraceApplyDetailOrderList
            );
            updateWrapper.set(LogisticsTraceOrder::getOrderStatus, LogisticsTraceOrderOrderStatusEnum.S3.getIntValue());

            response.setSourceOrderType(logisticsTraceOrderInfo.getSourceOrderType());
            response.setSourceOrderNos(logisticsTraceApplyOrderList.stream().map(LogisticsTraceApplyOrder::getSourceOrderNo).collect(Collectors.toList()));
        }
        updateWrapper.set(LogisticsTraceOrder::getBizConfig, logisticsBizConfigManager.getLogisticsBizConfigString(logisticsBizConfigInfo));
        logisticsTraceOrderMapper.update(null, updateWrapper);
        return response;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void backLogisticsTraceOrderPreviousStep(LogisticsTraceOrderBackPreviousStepRequest request) {
        LogisticsTraceOrderInfo logisticsTraceOrderInfo = logisticsTraceOrderMapper.getLogisticsTraceOrderById(request.getId());
        LogisticsBizConfigInfo logisticsBizConfigInfo = logisticsBizConfigManager.getLogisticsBizConfigInfo(logisticsTraceOrderInfo.getBizConfig());
        if (
                logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.A.getCode()) ||
                        logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.H2.getCode()) ||
                        logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.I.getCode()) ||
                        logisticsBizConfigInfo.isWbckConfirmed() || logisticsBizConfigInfo.isWbrkConfirmed() ||
                        logisticsBizConfigInfo.isRkConfirmed()
//                        || (logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.C.getCode()) &&
//                                logisticsBizConfigInfo.isCkFinished())
        ) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_BACK_PREVIOUS_STEP_STATUS_ERR);
        }
        String[] stepCodes = logisticsTraceOrderInfo.getStepType().split(",");
        int currentStepIndex = Arrays.binarySearch(stepCodes, logisticsTraceOrderInfo.getStepStatus());
        String previousStepStatus = stepCodes[currentStepIndex - 1];

        if (previousStepStatus.equals(LogisticsTraceStepEnum.C.getCode()) && logisticsBizConfigInfo.isCkFinished()) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_BACK_PREVIOUS_STEP_STATUS_ERR);
        }

        LambdaUpdateWrapper<LogisticsTraceOrder> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(LogisticsTraceOrder::getId, request.getId());
        updateWrapper.set(LogisticsTraceOrder::getStepStatus, previousStepStatus);

        if (logisticsBizConfigInfo.isWbckApplied() && logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.B.getCode())) {
            // 取消外部出库申请单
            WarehouseInter warehouseInter = warehouseClient.getWarehouseService(logisticsTraceOrderInfo.getSourceWarehouseCode());
            if (warehouseInter != null) {
                List<StockOutOrder> stockOutOrderList = stockOutOrderMapper.selectList(
                        new LambdaQueryWrapper<StockOutOrder>()
                                .eq(StockOutOrder::getOrderNo, logisticsTraceOrderInfo.getOrderNo())
                                .eq(StockOutOrder::getOutStockStatus, StockOutOrderOutStockStatusEnum.ACCEPT.getIntValue())
                                .eq(StockOutOrder::getIsDeleted, false)
                );
                if (CollectionUtils.isNotEmpty(stockOutOrderList)) {
                    int countSucceed = 0;
                    for (StockOutOrder stockOutOrder : stockOutOrderList) {
                        WarehouseCancelOutStockRequest cancelOutStockRequest = new WarehouseCancelOutStockRequest();
                        cancelOutStockRequest.setOutStockType(this.getEtonbaoOutOrderType(logisticsTraceOrderInfo));
                        cancelOutStockRequest.setOrderNo(stockOutOrder.getOutOrderNo());
                        cancelOutStockRequest.setOwnerCode(stockOutOrder.getOwnerCode());
                        WarehouseCancelOutStockResponse cancelOutStockResponse = warehouseInter.cancelOutStockOrder(cancelOutStockRequest);
                        if (cancelOutStockResponse.getSucceed()) {
                            LambdaUpdateWrapper<StockOutOrder> stockOutOrderUpdateWrapper = new LambdaUpdateWrapper<>();
                            stockOutOrderUpdateWrapper.eq(StockOutOrder::getId, stockOutOrder.getId());
                            stockOutOrderUpdateWrapper.set(StockOutOrder::getIsDeleted, true);
                            stockOutOrderMapper.update(null, stockOutOrderUpdateWrapper);

                            LambdaUpdateWrapper<StockOutOrderDetail> stockOutOrderDetailUpdateWrapper = new LambdaUpdateWrapper<>();
                            stockOutOrderDetailUpdateWrapper.eq(StockOutOrderDetail::getOutOrderNo, stockOutOrder.getOutOrderNo());
                            stockOutOrderDetailUpdateWrapper.set(StockOutOrderDetail::getIsDeleted, true);
                            stockOutOrderDetailMapper.update(null, stockOutOrderDetailUpdateWrapper);
                            countSucceed++;
                        }
                    }
                    if (countSucceed == stockOutOrderList.size()) {
                        logisticsBizConfigInfo.setWbckApplied(false);
                    }
                }
            }
        }

        if (logisticsBizConfigInfo.isWbrkApplied() && logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.F.getCode())) {
            // 取消外部入库申请单
            WarehouseInter warehouseInter = warehouseClient.getWarehouseService(logisticsTraceOrderInfo.getTargetWarehouseCode());
            if (warehouseInter != null) {
                List<StockInOrder> stockInOrderList = stockInOrderMapper.selectList(
                        new LambdaQueryWrapper<StockInOrder>()
                                .eq(StockInOrder::getOrderNo, logisticsTraceOrderInfo.getOrderNo())
                                .eq(StockInOrder::getInStockStatus, StockInOrderInStockStatusEnum.ACCEPT.getIntValue())
                                .eq(StockInOrder::getIsDeleted, false)
                );
                if (CollectionUtils.isNotEmpty(stockInOrderList)) {
                    int countSucceed = 0;
                    for (StockInOrder stockInOrder : stockInOrderList) {
                        WarehouseCancelInStockRequest cancelInStockRequest = new WarehouseCancelInStockRequest();
                        cancelInStockRequest.setInStockType(this.getEtonbaoInOrderType(logisticsTraceOrderInfo));
                        cancelInStockRequest.setOrderNo(stockInOrder.getOutOrderNo());
                        cancelInStockRequest.setOwnerCode(stockInOrder.getOwnerCode());
                        WarehouseCancelInStockResponse cancelInStockResponse = warehouseInter.cancelInStockOrder(cancelInStockRequest);
                        if (cancelInStockResponse.getSucceed()) {
                            LambdaUpdateWrapper<StockInOrder> stockInOrderUpdateWrapper = new LambdaUpdateWrapper<>();
                            stockInOrderUpdateWrapper.eq(StockInOrder::getId, stockInOrder.getId());
                            stockInOrderUpdateWrapper.set(StockInOrder::getIsDeleted, true);
                            stockInOrderMapper.update(null, stockInOrderUpdateWrapper);

                            LambdaUpdateWrapper<StockInOrderDetail> stockInOrderDetailUpdateWrapper = new LambdaUpdateWrapper<>();
                            stockInOrderDetailUpdateWrapper.eq(StockInOrderDetail::getOutOrderNo, stockInOrder.getOutOrderNo());
                            stockInOrderDetailUpdateWrapper.set(StockInOrderDetail::getIsDeleted, true);
                            stockInOrderDetailMapper.update(null, stockInOrderDetailUpdateWrapper);
                            countSucceed++;
                        }
                    }
                    if (countSucceed == stockInOrderList.size()) {
                        logisticsBizConfigInfo.setWbrkApplied(false);
                    }
                }
            }
        }

        updateWrapper.set(LogisticsTraceOrder::getBizConfig, logisticsBizConfigManager.getLogisticsBizConfigString(logisticsBizConfigInfo));
        logisticsTraceOrderMapper.update(null, updateWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteLogisticsTraceOrder(Long logisticsTraceOrderId) {
        LogisticsTraceOrderInfo logisticsTraceOrderInfo = logisticsTraceOrderMapper.getLogisticsTraceOrderById(logisticsTraceOrderId);
        if (!logisticsTraceOrderInfo.getOrderStatus().equals(LogisticsTraceOrderOrderStatusEnum.S1.getIntValue())) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_DELETE_STATUS_ERR);
        }

        // 逻辑删除物流跟进单
        LambdaUpdateWrapper<LogisticsTraceOrder> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(LogisticsTraceOrder::getId, logisticsTraceOrderId);
        updateWrapper.set(LogisticsTraceOrder::getIsDeleted, true);
        logisticsTraceOrderMapper.update(null, updateWrapper);

        // 逻辑删除原有的申请单明细
        LambdaUpdateWrapper<LogisticsTraceApplyDetailOrder> updateApplyDetailOrderWrapper = new LambdaUpdateWrapper<>();
        updateApplyDetailOrderWrapper.eq(LogisticsTraceApplyDetailOrder::getTraceOrderNo, logisticsTraceOrderInfo.getOrderNo());
        updateApplyDetailOrderWrapper.set(LogisticsTraceApplyDetailOrder::getIsDeleted, true);
        logisticsTraceApplyDetailOrderMapper.update(null, updateApplyDetailOrderWrapper);

        // 逻辑删除物流更新详情明细
        LambdaUpdateWrapper<LogisticsTraceDetailOrder> updateTraceDetailQueryWrapper = new LambdaUpdateWrapper<>();
        updateTraceDetailQueryWrapper.eq(LogisticsTraceDetailOrder::getTraceOrderNo, logisticsTraceOrderInfo.getOrderNo());
        updateTraceDetailQueryWrapper.set(LogisticsTraceDetailOrder::getIsDeleted, true);
        logisticsTraceDetailOrderMapper.update(null, updateTraceDetailQueryWrapper);
    }

    @Override
    public void submitLogisticsTraceOrderOA(LogisticsTraceOrderOaSubmitRequest request) {
        LogisticsTraceOrderInfo logisticsTraceOrderInfo = logisticsTraceOrderMapper.getLogisticsTraceOrderById(request.getId());
        if (!logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.H1.getCode())) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_OA_SUBMIT_STATUS_ERR);
        }
        LambdaUpdateWrapper<LogisticsTraceOrder> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(LogisticsTraceOrder::getId, request.getId());
        updateWrapper.set(LogisticsTraceOrder::getStepStatus, LogisticsTraceStepEnum.H2.getCode());
        updateWrapper.set(LogisticsTraceOrder::getFlowId, request.getFlowId());
        logisticsTraceOrderMapper.update(null, updateWrapper);
    }

    @Override
    public void approvedLogisticsTraceOrderOA(LogisticsTraceOrderOaApprovedRequest request) {
        LogisticsTraceOrderInfo logisticsTraceOrderInfo = logisticsTraceOrderMapper.getLogisticsTraceOrderByFlowId(request.getFlowId());
        if (logisticsTraceOrderInfo == null) {
            throw new BusinessException(CommonRespCode.PARAM_ERR, "流程ID有误！");
        }
        if (!logisticsTraceOrderInfo.getStepStatus().equals(LogisticsTraceStepEnum.H2.getCode())) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_OA_SUBMIT_STATUS_ERR);
        }

        LogisticsTraceOrder logisticsTraceOrder = new LogisticsTraceOrder();
        BeanUtils.copyProperties(logisticsTraceOrderInfo, logisticsTraceOrder);

        List<LogisticsTraceApplyOrder> logisticsTraceApplyOrderList = new ArrayList<>();
        logisticsTraceApplyOrderMapper.listLogisticsTraceApplyOrderByTraceOrderNo(logisticsTraceOrderInfo.getOrderNo()).forEach(item -> {
            LogisticsTraceApplyOrder logisticsTraceApplyOrder = new LogisticsTraceApplyOrder();
            BeanUtils.copyProperties(item, logisticsTraceApplyOrder);
            logisticsTraceApplyOrderList.add(logisticsTraceApplyOrder);
        });

        List<LogisticsTraceApplyDetailOrder> logisticsTraceApplyDetailOrderList = logisticsTraceApplyDetailOrderMapper.selectList(
                new LambdaQueryWrapper<LogisticsTraceApplyDetailOrder>()
                        .eq(LogisticsTraceApplyDetailOrder::getTraceOrderNo, logisticsTraceOrderInfo.getOrderNo())
                        .eq(LogisticsTraceApplyDetailOrder::getIsDeleted, false));

        this.inFinishing(
                logisticsTraceOrder,
                logisticsTraceApplyOrderList,
                logisticsTraceApplyDetailOrderList
        );

        LambdaUpdateWrapper<LogisticsTraceOrder> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(LogisticsTraceOrder::getId, logisticsTraceOrderInfo.getId());
        updateWrapper.set(LogisticsTraceOrder::getOrderStatus, LogisticsTraceOrderOrderStatusEnum.S3.getIntValue());
        logisticsTraceOrderMapper.update(null, updateWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void etonbaoInStockConfirm(WarehouseEtonbaoConfirmInStockRequest request) {
        // TODO 青岛仓易通宝平台入库确认场景业务逻辑处理
        LambdaQueryWrapper<StockInOrder> stockInOrderQueryWrapper = new LambdaQueryWrapper<>();
        stockInOrderQueryWrapper.eq(StockInOrder::getOutOrderNo, request.getEntryOrder().getEntryOrderId());
        stockInOrderQueryWrapper.eq(StockInOrder::getIsDeleted, false);
        StockInOrder stockInOrder = stockInOrderMapper.selectOne(stockInOrderQueryWrapper);
        if (stockInOrder == null) {
            throw new BusinessException(CommonRespCode.SYSTEM_ERR, "入库单ID无效！");
        }

        LambdaQueryWrapper<LogisticsTraceOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LogisticsTraceOrder::getOrderNo, stockInOrder.getOrderNo());
        LogisticsTraceOrder logisticsTraceOrder = logisticsTraceOrderMapper.selectOne(queryWrapper);
        if (logisticsTraceOrder == null) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_QUERY_NON_EXISTENT);
        }
        if (logisticsTraceOrder.getIsDeleted()) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_QUERY_IS_DELETE);
        }
        if (logisticsTraceOrder.getOrderStatus().equals(LogisticsTraceOrderOrderStatusEnum.S3.getIntValue())) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_FINISHED);
        }
        LogisticsBizConfigInfo logisticsBizConfigInfo = logisticsBizConfigManager.getLogisticsBizConfigInfo(logisticsTraceOrder.getBizConfig());
        if (logisticsBizConfigInfo.isWbrkConfirmed()) {
            throw new BusinessException(CommonRespCode.SYSTEM_ERR, "重复入库确认！");
        }

        List<EtonbaoInStockConfirmBatch> etonbaoInStockConfirmBatches = new ArrayList<>();
        // 批次日志处理
        for (WarehouseEtonbaoConfirmInStockRequest.OrderLine orderLine : request.getOrderLines()) {
            LambdaQueryWrapper<StockInOrderDetail> stockInOrderDetailQueryWrapper = new LambdaQueryWrapper<>();
            stockInOrderDetailQueryWrapper.eq(StockInOrderDetail::getOrderNo, logisticsTraceOrder.getOrderNo());
            stockInOrderDetailQueryWrapper.eq(StockInOrderDetail::getOutOrderNo, stockInOrder.getOutOrderNo());
            stockInOrderDetailQueryWrapper.eq(StockInOrderDetail::getMateItemId, orderLine.getItemId());
            stockInOrderDetailQueryWrapper.last(" limit 1");
            StockInOrderDetail stockInOrderDetail = stockInOrderDetailMapper.selectOne(stockInOrderDetailQueryWrapper);

            String mateSku = stockInOrderDetail != null ? stockInOrderDetail.getMateSku() : null;
            for (WarehouseEtonbaoConfirmInStockRequest.OrderLineBatch batch : orderLine.getBatchs()) {
                StockInOrderDetailBatch stockInOrderDetailBatch = new StockInOrderDetailBatch();
                stockInOrderDetailBatch.setOrderNo(logisticsTraceOrder.getOrderNo());
                stockInOrderDetailBatch.setOutOrderNo(stockInOrder.getOutOrderNo());
                stockInOrderDetailBatch.setMateSku(mateSku);
                stockInOrderDetailBatch.setMateItemId(orderLine.getItemId());
                stockInOrderDetailBatch.setActualQty(batch.getActualQty());
                stockInOrderDetailBatch.setBatchCode(batch.getBatchCode());
                stockInOrderDetailBatch.setProduceCode(batch.getProduceCode());
                WarehouseInventoryTypeEnum batchInventoryType = WarehouseInventoryTypeEnum.valueOfCode(batch.getInventoryType());
                if (batchInventoryType == null) {
                    batchInventoryType = WarehouseInventoryTypeEnum.CC;
                }
                switch (batchInventoryType) {
                    case ZP:
                        stockInOrderDetailBatch.setInventoryType(InventoryTypeEnum.ZP.getIntValue());
                        break;
                    case CC:
                        stockInOrderDetailBatch.setInventoryType(InventoryTypeEnum.CC.getIntValue());
                        break;
                    case XS:
                        stockInOrderDetailBatch.setInventoryType(InventoryTypeEnum.XS.getIntValue());
                        break;
                    case JS:
                        stockInOrderDetailBatch.setInventoryType(InventoryTypeEnum.JS.getIntValue());
                        break;
                    default:
                        break;
                }
                stockInOrderDetailBatch.setProductDate(batch.getProductDate());
                stockInOrderDetailBatch.setExpireDate(batch.getExpireDate());
                stockInOrderDetailBatchMapper.insert(stockInOrderDetailBatch);
                // 整理待分配的批次数据
                List<EtonbaoInStockConfirmBatch> filterEtonbaoInStockConfirmBatches = etonbaoInStockConfirmBatches
                        .stream()
                        .filter(item ->
                                item.getMateSku().equals(mateSku) &&
                                        (item.getProduceCode() == null || item.getProduceCode().equals(batch.getProduceCode())) &&
                                        item.getProductDate().equals(batch.getProductDate()) &&
                                        item.getExpireDate().equals(batch.getExpireDate()) &&
                                        item.getInventoryType().equals(batch.getInventoryType())
                        )
                        .collect(Collectors.toList());
                if (CollectionUtils.isEmpty(filterEtonbaoInStockConfirmBatches)) {
                    EtonbaoInStockConfirmBatch etonbaoInStockConfirmBatch = new EtonbaoInStockConfirmBatch();
                    BeanUtils.copyProperties(batch, etonbaoInStockConfirmBatch);
                    etonbaoInStockConfirmBatch.setMateSku(mateSku);
                    etonbaoInStockConfirmBatches.add(etonbaoInStockConfirmBatch);
                } else {
                    filterEtonbaoInStockConfirmBatches.get(0).setActualQty(filterEtonbaoInStockConfirmBatches.get(0).getActualQty() + batch.getActualQty());
                }
            }
        }
        // 物流跟进单相关数据处理
        if (logisticsTraceOrder.getSourceOrderType().equals(SubjectEnum.T11.getCode())) {
            // 采购入库
            if (CollectionUtils.isEmpty(logisticsTraceDetailOrderMapper.listLogisticsTraceDetailOrderByTraceOrderNo(logisticsTraceOrder.getOrderNo(), LogisticsTraceDetailOrderTypeEnum.T1.getIntValue()))) {
                List<LogisticsTraceApplyDetailOrderGetDetailResponse> logisticsTraceApplyDetailOrders = logisticsTraceApplyDetailOrderMapper.listLogisticsTraceApplyDetailOrderByTraceOrderNo(logisticsTraceOrder.getOrderNo());
                // 申请详情数据按渠道优先级排序
                logisticsTraceApplyDetailOrders.sort(
                        Comparator
                                .comparingInt(LogisticsTraceApplyDetailOrderGetDetailResponse::getTraceQty)
                                .reversed()
                );
                // 分配
                List<LogisticsTraceDetailOrder> traceDetailOrderList = new ArrayList<>();
                for (LogisticsTraceApplyDetailOrderGetDetailResponse logisticsTraceApplyDetailOrder : logisticsTraceApplyDetailOrders) {
                    // 分配完成标识
                    boolean finished = false;
                    // 分配正品
                    List<EtonbaoInStockConfirmBatch> zpEtonbaoInStockConfirmBatches = etonbaoInStockConfirmBatches
                            .stream()
                            .filter(item ->
                                    item.getMateSku().equals(logisticsTraceApplyDetailOrder.getMateSku()) &&
                                            item.getActualQty() > 0 &&
                                            item.getInventoryType().equals(WarehouseInventoryTypeEnum.ZP.getCode()))
                            .collect(Collectors.toList())
                            .stream()
                            .sorted(Comparator.comparingInt(EtonbaoInStockConfirmBatch::getActualQty))
                            .collect(Collectors.toList());
                    for (EtonbaoInStockConfirmBatch zpEtonbaoInStockConfirmBatch : zpEtonbaoInStockConfirmBatches) {
                        Integer actualInQty;
                        if (zpEtonbaoInStockConfirmBatch.getActualQty() <= logisticsTraceApplyDetailOrder.getTraceQty()) {
                            actualInQty = zpEtonbaoInStockConfirmBatch.getActualQty();
                        } else {
                            actualInQty = logisticsTraceApplyDetailOrder.getTraceQty();
                        }
                        List<LogisticsTraceDetailOrder> filterZpTraceDetailOrderList = traceDetailOrderList
                                .stream()
                                .filter(item ->
                                        item.getApplyDetailId().equals(logisticsTraceApplyDetailOrder.getId()) &&
                                                item.getProduceCode().equals(zpEtonbaoInStockConfirmBatch.getProduceCode()) &&
                                                item.getProductDate().equals(zpEtonbaoInStockConfirmBatch.getProductDate()) &&
                                                item.getExpireDate().equals(zpEtonbaoInStockConfirmBatch.getExpireDate()))
                                .collect(Collectors.toList());
                        if (CollectionUtils.isEmpty(filterZpTraceDetailOrderList)) {
                            LogisticsTraceDetailOrder logisticsTraceDetailOrder = new LogisticsTraceDetailOrder();
                            logisticsTraceDetailOrder.setApplyDetailId(logisticsTraceApplyDetailOrder.getApplyDetailId());
                            logisticsTraceDetailOrder.setTraceOrderNo(logisticsTraceApplyDetailOrder.getTraceOrderNo());
                            logisticsTraceDetailOrder.setApplyOrderNo(logisticsTraceApplyDetailOrder.getApplyOrderNo());
                            logisticsTraceDetailOrder.setOrderType(LogisticsTraceDetailOrderTypeEnum.T1.getIntValue());
                            logisticsTraceDetailOrder.setMateSku(logisticsTraceApplyDetailOrder.getMateSku());
                            logisticsTraceDetailOrder.setSubWarehouseCode(logisticsTraceApplyDetailOrder.getSourceSubWarehouseCode());
                            logisticsTraceDetailOrder.setInventoryType(InventoryTypeEnum.ZP.getIntValue());
                            logisticsTraceDetailOrder.setPurchaseOrderNo(logisticsTraceApplyDetailOrder.getPurchaseOrderNo());
                            logisticsTraceDetailOrder.setPurchaseDetailOrderNo(logisticsTraceApplyDetailOrder.getPurchaseDetailOrderNo());
                            logisticsTraceDetailOrder.setCostPrice(logisticsTraceApplyDetailOrder.getCostPrice());
                            logisticsTraceDetailOrder.setProduceCode(zpEtonbaoInStockConfirmBatch.getProduceCode());
                            logisticsTraceDetailOrder.setActualInQty(actualInQty);
                            logisticsTraceDetailOrder.setProductDate(zpEtonbaoInStockConfirmBatch.getProductDate());
                            logisticsTraceDetailOrder.setExpireDate(zpEtonbaoInStockConfirmBatch.getExpireDate());
                            traceDetailOrderList.add(logisticsTraceDetailOrder);
                        } else {
                            filterZpTraceDetailOrderList.get(0).setActualInQty(filterZpTraceDetailOrderList.get(0).getActualInQty() + actualInQty);
                        }
                        zpEtonbaoInStockConfirmBatch.setActualQty(zpEtonbaoInStockConfirmBatch.getActualQty() - actualInQty);
                        logisticsTraceApplyDetailOrder.setTraceQty(logisticsTraceApplyDetailOrder.getTraceQty() - actualInQty);
                        if (logisticsTraceApplyDetailOrder.getTraceQty() == 0) {
                            finished = true;
                            break;
                        }
                    }
                    if (!finished) {
                        // 正品不够，分配瑕疵
                        List<EtonbaoInStockConfirmBatch> ccEtonbaoInStockConfirmBatches = etonbaoInStockConfirmBatches
                                .stream()
                                .filter(item ->
                                        item.getMateSku().equals(logisticsTraceApplyDetailOrder.getMateSku()) &&
                                                item.getActualQty() > 0 &&
                                                !item.getInventoryType().equals(WarehouseInventoryTypeEnum.ZP.getCode()))
                                .collect(Collectors.toList())
                                .stream()
                                .sorted(Comparator.comparingInt(EtonbaoInStockConfirmBatch::getActualQty))
                                .collect(Collectors.toList());
                        for (EtonbaoInStockConfirmBatch ccEtonbaoInStockConfirmBatch : ccEtonbaoInStockConfirmBatches) {
                            Integer actualInQty;
                            if (ccEtonbaoInStockConfirmBatch.getActualQty() <= logisticsTraceApplyDetailOrder.getTraceQty()) {
                                actualInQty = ccEtonbaoInStockConfirmBatch.getActualQty();
                            } else {
                                actualInQty = logisticsTraceApplyDetailOrder.getTraceQty();
                            }
                            List<LogisticsTraceDetailOrder> filterCcTraceDetailOrderList = traceDetailOrderList
                                    .stream()
                                    .filter(item ->
                                            item.getApplyDetailId().equals(logisticsTraceApplyDetailOrder.getId()) &&
                                                    item.getProduceCode().equals(ccEtonbaoInStockConfirmBatch.getProduceCode()) &&
                                                    item.getProductDate().equals(ccEtonbaoInStockConfirmBatch.getProductDate()) &&
                                                    item.getExpireDate().equals(ccEtonbaoInStockConfirmBatch.getExpireDate()))
                                    .collect(Collectors.toList());
                            if (CollectionUtils.isEmpty(filterCcTraceDetailOrderList)) {
                                LogisticsTraceDetailOrder logisticsTraceDetailOrder = new LogisticsTraceDetailOrder();
                                logisticsTraceDetailOrder.setApplyDetailId(logisticsTraceApplyDetailOrder.getApplyDetailId());
                                logisticsTraceDetailOrder.setTraceOrderNo(logisticsTraceApplyDetailOrder.getTraceOrderNo());
                                logisticsTraceDetailOrder.setApplyOrderNo(logisticsTraceApplyDetailOrder.getApplyOrderNo());
                                logisticsTraceDetailOrder.setOrderType(LogisticsTraceDetailOrderTypeEnum.T1.getIntValue());
                                logisticsTraceDetailOrder.setMateSku(logisticsTraceApplyDetailOrder.getMateSku());
                                logisticsTraceDetailOrder.setSubWarehouseCode(logisticsTraceApplyDetailOrder.getSourceSubWarehouseCode());
                                logisticsTraceDetailOrder.setInventoryType(InventoryTypeEnum.CC.getIntValue());
                                logisticsTraceDetailOrder.setPurchaseOrderNo(logisticsTraceApplyDetailOrder.getPurchaseOrderNo());
                                logisticsTraceDetailOrder.setPurchaseDetailOrderNo(logisticsTraceApplyDetailOrder.getPurchaseDetailOrderNo());
                                logisticsTraceDetailOrder.setCostPrice(logisticsTraceApplyDetailOrder.getCostPrice());
                                logisticsTraceDetailOrder.setProduceCode(ccEtonbaoInStockConfirmBatch.getProduceCode());
                                logisticsTraceDetailOrder.setActualInQty(actualInQty);
                                logisticsTraceDetailOrder.setProductDate(ccEtonbaoInStockConfirmBatch.getProductDate());
                                logisticsTraceDetailOrder.setExpireDate(ccEtonbaoInStockConfirmBatch.getExpireDate());
                                traceDetailOrderList.add(logisticsTraceDetailOrder);
                            } else {
                                filterCcTraceDetailOrderList.get(0).setActualInQty(filterCcTraceDetailOrderList.get(0).getActualInQty() + actualInQty);
                            }
                            ccEtonbaoInStockConfirmBatch.setActualQty(ccEtonbaoInStockConfirmBatch.getActualQty() - actualInQty);
                            logisticsTraceApplyDetailOrder.setTraceQty(logisticsTraceApplyDetailOrder.getTraceQty() - actualInQty);
                            if (logisticsTraceApplyDetailOrder.getTraceQty() == 0) {
                                break;
                            }
                        }
                    }
                }
                if (CollectionUtils.isNotEmpty(traceDetailOrderList)) {
                    // 保存分配后的入库记录
                    for (LogisticsTraceDetailOrder traceDetailOrder : traceDetailOrderList) {
                        logisticsTraceDetailOrderMapper.insert(traceDetailOrder);
                    }
                }
            }
        }

        // 更新物流跟进单
        logisticsBizConfigInfo.setWbrkConfirmed(true);
        LambdaUpdateWrapper<LogisticsTraceOrder> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(LogisticsTraceOrder::getId, logisticsTraceOrder.getId());
        updateWrapper.set(LogisticsTraceOrder::getBizConfig, logisticsBizConfigManager.getLogisticsBizConfigString(logisticsBizConfigInfo));
        logisticsTraceOrderMapper.update(null, updateWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void etonbaoOutStockConfirm(WarehouseEtonbaoConfirmOutStockRequest request) {
        // TODO 青岛仓易通宝平台出库确认场景业务逻辑处理
        LambdaQueryWrapper<StockOutOrder> stockOutOrderQueryWrapper = new LambdaQueryWrapper<>();
        stockOutOrderQueryWrapper.eq(StockOutOrder::getOutOrderNo, request.getDeliveryOrder().getDeliveryOrderId());
        stockOutOrderQueryWrapper.eq(StockOutOrder::getIsDeleted, false);
        StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(stockOutOrderQueryWrapper);
        if (stockOutOrder == null) {
            throw new BusinessException(CommonRespCode.SYSTEM_ERR, "出库单ID无效！");
        }

        LambdaQueryWrapper<LogisticsTraceOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LogisticsTraceOrder::getOrderNo, stockOutOrder.getOrderNo());
        LogisticsTraceOrder logisticsTraceOrder = logisticsTraceOrderMapper.selectOne(queryWrapper);
        if (logisticsTraceOrder == null) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_QUERY_NON_EXISTENT);
        }
        if (logisticsTraceOrder.getIsDeleted()) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_QUERY_IS_DELETE);
        }
        if (logisticsTraceOrder.getOrderStatus().equals(LogisticsTraceOrderOrderStatusEnum.S3.getIntValue())) {
            throw new BusinessException(WarehouseRespCode.LOGISTICS_TRACE_ORDER_FINISHED);
        }
        LogisticsBizConfigInfo logisticsBizConfigInfo = logisticsBizConfigManager.getLogisticsBizConfigInfo(logisticsTraceOrder.getBizConfig());
        if (logisticsBizConfigInfo.isWbckConfirmed()) {
            throw new BusinessException(CommonRespCode.SYSTEM_ERR, "重复出库确认！");
        }

        // 批次日志处理
        for (WarehouseEtonbaoConfirmOutStockRequest.OrderLine orderLine : request.getOrderLines()) {
            LambdaQueryWrapper<StockOutOrderDetail> stockOutOrderDetailQueryWrapper = new LambdaQueryWrapper<>();
            stockOutOrderDetailQueryWrapper.eq(StockOutOrderDetail::getOrderNo, logisticsTraceOrder.getOrderNo());
            stockOutOrderDetailQueryWrapper.eq(StockOutOrderDetail::getOutOrderNo, stockOutOrder.getOutOrderNo());
            stockOutOrderDetailQueryWrapper.eq(StockOutOrderDetail::getMateItemId, orderLine.getItemId());
            stockOutOrderDetailQueryWrapper.last(" limit 1");
            StockOutOrderDetail stockOutOrderDetail = stockOutOrderDetailMapper.selectOne(stockOutOrderDetailQueryWrapper);

            String mateSku = stockOutOrderDetail != null ? stockOutOrderDetail.getMateSku() : null;
            if (CollectionUtils.isEmpty(orderLine.getBatchs())) {
                StockOutOrderDetailBatch stockOutOrderDetailBatch = new StockOutOrderDetailBatch();
                stockOutOrderDetailBatch.setOrderNo(logisticsTraceOrder.getOrderNo());
                stockOutOrderDetailBatch.setOutOrderNo(stockOutOrder.getOutOrderNo());
                stockOutOrderDetailBatch.setMateSku(mateSku);
                stockOutOrderDetailBatch.setMateItemId(orderLine.getItemId());
                stockOutOrderDetailBatch.setActualQty(orderLine.getActualQty());
                WarehouseInventoryTypeEnum batchInventoryType = WarehouseInventoryTypeEnum.valueOfCode(orderLine.getInventoryType());
                if (batchInventoryType == null) {
                    batchInventoryType = WarehouseInventoryTypeEnum.CC;
                }
                switch (batchInventoryType) {
                    case ZP:
                        stockOutOrderDetailBatch.setInventoryType(InventoryTypeEnum.ZP.getIntValue());
                        break;
                    case CC:
                        stockOutOrderDetailBatch.setInventoryType(InventoryTypeEnum.CC.getIntValue());
                        break;
                    case XS:
                        stockOutOrderDetailBatch.setInventoryType(InventoryTypeEnum.XS.getIntValue());
                        break;
                    case JS:
                        stockOutOrderDetailBatch.setInventoryType(InventoryTypeEnum.JS.getIntValue());
                        break;
                    default:
                        break;
                }
                stockOutOrderDetailBatchMapper.insert(stockOutOrderDetailBatch);
            } else {
                for (WarehouseEtonbaoConfirmOutStockRequest.OrderLineBatch batch : orderLine.getBatchs()) {
                    StockOutOrderDetailBatch stockOutOrderDetailBatch = new StockOutOrderDetailBatch();
                    stockOutOrderDetailBatch.setOrderNo(logisticsTraceOrder.getOrderNo());
                    stockOutOrderDetailBatch.setOutOrderNo(stockOutOrder.getOutOrderNo());
                    stockOutOrderDetailBatch.setMateSku(mateSku);
                    stockOutOrderDetailBatch.setMateItemId(orderLine.getItemId());
                    stockOutOrderDetailBatch.setActualQty(batch.getActualQty());
                    WarehouseInventoryTypeEnum batchInventoryType = WarehouseInventoryTypeEnum.valueOfCode(batch.getInventoryType());
                    if (batchInventoryType == null) {
                        batchInventoryType = WarehouseInventoryTypeEnum.CC;
                    }
                    switch (batchInventoryType) {
                        case ZP:
                            stockOutOrderDetailBatch.setInventoryType(InventoryTypeEnum.ZP.getIntValue());
                            break;
                        case CC:
                            stockOutOrderDetailBatch.setInventoryType(InventoryTypeEnum.CC.getIntValue());
                            break;
                        case XS:
                            stockOutOrderDetailBatch.setInventoryType(InventoryTypeEnum.XS.getIntValue());
                            break;
                        case JS:
                            stockOutOrderDetailBatch.setInventoryType(InventoryTypeEnum.JS.getIntValue());
                            break;
                        default:
                            break;
                    }
                    stockOutOrderDetailBatch.setProductDate(batch.getProductDate());
                    stockOutOrderDetailBatch.setExpireDate(batch.getExpireDate());
                    stockOutOrderDetailBatchMapper.insert(stockOutOrderDetailBatch);
                }
            }
        }

        // 物流跟进单逻辑处理
        if (logisticsTraceOrder.getStepStatus().equals(LogisticsTraceStepEnum.B.getCode())) {
//            List<LogisticsTraceApplyOrder> logisticsTraceApplyOrderList = new ArrayList<>();
//            logisticsTraceApplyOrderMapper.listLogisticsTraceApplyOrderByTraceOrderNo(logisticsTraceOrder.getOrderNo()).forEach(item -> {
//                LogisticsTraceApplyOrder logisticsTraceApplyOrder = new LogisticsTraceApplyOrder();
//                BeanUtils.copyProperties(item, logisticsTraceApplyOrder);
//                logisticsTraceApplyOrderList.add(logisticsTraceApplyOrder);
//            });

//            List<LogisticsTraceApplyDetailOrder> logisticsTraceApplyDetailOrderList = logisticsTraceApplyDetailOrderMapper.selectList(
//                    new LambdaQueryWrapper<LogisticsTraceApplyDetailOrder>()
//                            .eq(LogisticsTraceApplyDetailOrder::getTraceOrderNo, logisticsTraceOrder.getOrderNo())
//                            .eq(LogisticsTraceApplyDetailOrder::getIsDeleted, false));

            // 出库逻辑
//            this.outFinishing(
//                    logisticsTraceOrder,
//                    logisticsTraceApplyOrderList,
//                    logisticsTraceApplyDetailOrderList,
//                    logisticsBizConfigInfo
//            );

            logisticsBizConfigInfo.setWbckConfirmed(true);
            String[] stepCodes = logisticsTraceOrder.getStepType().split(",");
            int currentStepIndex = Arrays.binarySearch(stepCodes, logisticsTraceOrder.getStepStatus());
            String nextStepStatus = stepCodes[currentStepIndex + 1];

            LambdaUpdateWrapper<LogisticsTraceOrder> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(LogisticsTraceOrder::getId, logisticsTraceOrder.getId());
            updateWrapper.set(LogisticsTraceOrder::getStepStatus, nextStepStatus);
            updateWrapper.set(LogisticsTraceOrder::getBizConfig, logisticsBizConfigManager.getLogisticsBizConfigString(logisticsBizConfigInfo));
            logisticsTraceOrderMapper.update(null, updateWrapper);
        }
    }

    /**
     * @param logisticsTraceOrder
     * @param logisticsTraceApplyOrderList
     * @param logisticsTraceApplyDetailOrderList
     */
    private void inFinishing(
            LogisticsTraceOrder logisticsTraceOrder,
            List<LogisticsTraceApplyOrder> logisticsTraceApplyOrderList,
            List<LogisticsTraceApplyDetailOrder> logisticsTraceApplyDetailOrderList
    ) {
        List<LogisticsTraceDetailOrder> logisticsTraceDetailOrderList = logisticsTraceDetailOrderMapper.selectList(
                new LambdaQueryWrapper<LogisticsTraceDetailOrder>()
                        .eq(LogisticsTraceDetailOrder::getTraceOrderNo, logisticsTraceOrder.getOrderNo())
                        .eq(LogisticsTraceDetailOrder::getIsDeleted, false)
        );
        if (logisticsTraceOrder.getSourceOrderType().equals(SubjectEnum.T11.getCode())) {
            // 采购入库
            mateInventoryManager.t11(
                    logisticsTraceOrder,
                    logisticsTraceApplyOrderList,
                    logisticsTraceApplyDetailOrderList,
                    logisticsTraceDetailOrderList
            );
        } else if (logisticsTraceOrder.getSourceOrderType().equals(SubjectEnum.T32.getCode())) {
            // 物流调拨入库
            Pair<String, List<MateInventoryAutoAdjustItemModel>> pair = mateInventoryManager.t31Confirm(
                    logisticsTraceOrder,
                    logisticsTraceApplyOrderList,
                    logisticsTraceApplyDetailOrderList,
                    logisticsTraceDetailOrderList
            );
            List<MateInventoryAutoAdjustItemModel> itemModels = pair.getValue();
            String subWarehouseCode = pair.getKey();
            // 自动矫正
            if (CollectionUtils.isNotEmpty(itemModels)) {
                MateInventoryAutoAdjustModel model = new MateInventoryAutoAdjustModel();
                model.setSubWarehouseCode(subWarehouseCode);
                model.setAdjustType(MateInventoryAdjustLogAdjustTypeEnum.T3.getIntValue());
                model.setAdjustReason("调拨自动矫正");
                model.setItems(itemModels);
                mateInventoryAdjustManager.autoAdjust(model);
            }
        } else if (logisticsTraceOrder.getSourceOrderType().equals(SubjectEnum.T41.getCode()) || logisticsTraceOrder.getSourceOrderType().equals(SubjectEnum.T71.getCode())) {
            // 预售退货/销售退货
            mateInventoryManager.t41OrT71OrT31(
                    logisticsTraceOrder,
                    logisticsTraceApplyOrderList,
                    logisticsTraceApplyDetailOrderList,
                    logisticsTraceDetailOrderList
            );
        }
    }

    /**
     * @param logisticsTraceOrder
     * @param logisticsTraceApplyOrderList
     * @param logisticsTraceApplyDetailOrderList
     * @param logisticsBizConfigInfo
     */
    private void outFinishing(
            LogisticsTraceOrder logisticsTraceOrder,
            List<LogisticsTraceApplyOrder> logisticsTraceApplyOrderList,
            List<LogisticsTraceApplyDetailOrder> logisticsTraceApplyDetailOrderList,
            LogisticsBizConfigInfo logisticsBizConfigInfo
    ) {
        if (!logisticsBizConfigInfo.isCkFinished()) {
            // 创建出库跟进明细数据
            List<LogisticsTraceDetailOrder> outTraceDetailOrderList = new ArrayList<>();
            List<LogisticsTraceApplyDetailOrderGetDetailResponse> traceApplyDetailOrderList = logisticsTraceApplyDetailOrderMapper
                    .listLogisticsTraceApplyDetailOrderByTraceOrderNo(logisticsTraceOrder.getOrderNo());
            List<MateInventoryDetailInfo> list = mateInventoryMapper.listMateInventoryByIdList(traceApplyDetailOrderList
                    .stream().map(LogisticsTraceApplyDetailOrderGetDetailResponse::getMateInventoryId).collect(Collectors.toList()));
            Map<Long, MateInventoryDetailInfo> mateInventoryDetailMap = list.stream().collect(Collectors.toMap(MateInventoryDetailInfo::getId, info -> info));
            for (LogisticsTraceApplyDetailOrderGetDetailResponse traceApplyDetailOrderInfo : traceApplyDetailOrderList) {
                LogisticsTraceDetailOrder logisticsTraceDetailOrder = new LogisticsTraceDetailOrder();
                logisticsTraceDetailOrder.setApplyDetailId(traceApplyDetailOrderInfo.getApplyDetailId());
                logisticsTraceDetailOrder.setTraceOrderNo(traceApplyDetailOrderInfo.getTraceOrderNo());
                logisticsTraceDetailOrder.setApplyOrderNo(traceApplyDetailOrderInfo.getApplyOrderNo());
                logisticsTraceDetailOrder.setOrderType(LogisticsTraceDetailOrderTypeEnum.T2.getIntValue());
                logisticsTraceDetailOrder.setMateSku(traceApplyDetailOrderInfo.getMateSku());
                logisticsTraceDetailOrder.setSubWarehouseCode(traceApplyDetailOrderInfo.getSourceSubWarehouseCode());
                logisticsTraceDetailOrder.setPurchaseCode(traceApplyDetailOrderInfo.getPurchaseCode());
                logisticsTraceDetailOrder.setInventoryType(traceApplyDetailOrderInfo.getInventoryType() != null ? traceApplyDetailOrderInfo.getInventoryType() : InventoryTypeEnum.ZP.getIntValue());
                logisticsTraceDetailOrder.setActualInQty(traceApplyDetailOrderInfo.getTraceQty());
                logisticsTraceDetailOrder.setMateInventoryId(traceApplyDetailOrderInfo.getMateInventoryId());
                logisticsTraceDetailOrder.setPurchaseOrderNo(traceApplyDetailOrderInfo.getPurchaseOrderNo());
                logisticsTraceDetailOrder.setPurchaseDetailOrderNo(traceApplyDetailOrderInfo.getPurchaseDetailOrderNo());
                logisticsTraceDetailOrder.setCostPrice(traceApplyDetailOrderInfo.getCostPrice());
                MateInventoryDetailInfo mateInventoryDetailInfo = mateInventoryDetailMap.get(traceApplyDetailOrderInfo.getMateInventoryId());
                logisticsTraceDetailOrder.setCountryOfOrigin(mateInventoryDetailInfo.getCountryOfOrigin());
                logisticsTraceDetailOrder.setProduceCode(mateInventoryDetailInfo.getProduceCode());
                logisticsTraceDetailOrder.setProductDate(mateInventoryDetailInfo.getProductDate());
                logisticsTraceDetailOrder.setExpireDate(mateInventoryDetailInfo.getExpireDate());
                // 创建调拨关联的入库单
                logisticsTraceDetailOrderMapper.insert(logisticsTraceDetailOrder);
                outTraceDetailOrderList.add(logisticsTraceDetailOrder);
            }
            if (logisticsTraceOrder.getSourceOrderType().equals(SubjectEnum.T32.getCode())) {
                // 物流调拨
                // 扣减冻结的库存
                mateInventoryManager.t12OrT42OrT72OrT32(
                        logisticsTraceOrder,
                        logisticsTraceApplyOrderList,
                        logisticsTraceApplyDetailOrderList,
                        outTraceDetailOrderList
                );
                // 目的库存需要增加在途数量
                mateInventoryManager.t31Apply(
                        logisticsTraceOrder,
                        logisticsTraceApplyOrderList,
                        logisticsTraceApplyDetailOrderList,
                        outTraceDetailOrderList
                );
            } else if (
                    logisticsTraceOrder.getSourceOrderType().equals(SubjectEnum.T12.getCode()) ||
                            logisticsTraceOrder.getSourceOrderType().equals(SubjectEnum.T42.getCode()) ||
                            logisticsTraceOrder.getSourceOrderType().equals(SubjectEnum.T72.getCode())
            ) {
                // 采购退货出库 或 tob预售单 或 销售单
                // 扣减冻结的库存
                mateInventoryManager.t12OrT42OrT72OrT32(
                        logisticsTraceOrder,
                        logisticsTraceApplyOrderList,
                        logisticsTraceApplyDetailOrderList,
                        outTraceDetailOrderList
                );
            }
            logisticsBizConfigInfo.setCkFinished(true);
        }
    }

    /**
     * 外部仓库入库申请
     *
     * @param logisticsTraceOrderInfo
     */
    private int externalWarehouseInApply(LogisticsTraceOrderInfo logisticsTraceOrderInfo) {
        int countSucceed = 0;
        List<LogisticsTraceApplyDetailOrderGetDetailResponse> traceApplyDetailOrderList = logisticsTraceApplyDetailOrderMapper
                .listLogisticsTraceApplyDetailOrderByTraceOrderNo(logisticsTraceOrderInfo.getOrderNo());
        if (CollectionUtils.isNotEmpty(traceApplyDetailOrderList)) {
            WarehouseInter warehouseInter = warehouseClient.getWarehouseService(logisticsTraceOrderInfo.getTargetWarehouseCode());
            if (warehouseInter != null) {
                List<WarehouseAddInStockRequest> warehouseAddInStockRequests = new ArrayList<>();
                traceApplyDetailOrderList.forEach(traceApplyDetailOrder -> {
                    SubWarehouseInfo inSubWarehouseInfo;
                    if (logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T11.getCode())) {
                        // 采购入库
                        inSubWarehouseInfo = subWarehouseMapper.getSubWarehouseByCode(traceApplyDetailOrder.getBkSubWarehouseCode());
                    } else {
                        String inSubWarehouseCode = StringUtils.isNotBlank(traceApplyDetailOrder.getTargetSubWarehouseCode()) ? traceApplyDetailOrder.getTargetSubWarehouseCode() : traceApplyDetailOrder.getSourceSubWarehouseCode();
                        inSubWarehouseInfo = subWarehouseMapper.getSubWarehouseByCode(inSubWarehouseCode);
                    }
                    WarehouseBookInfo inWarehouseBookInfo = warehouseBookMapper.getWarehouseBookById(inSubWarehouseInfo.getWarehouseBookId());
                    WarehouseInStockItemInfo itemInfo = new WarehouseInStockItemInfo();
                    itemInfo.setOrderLineNo(IdUtil.fastSimpleUUID());
//                    itemInfo.setInventoryType(traceApplyDetailOrder.getInventoryType().equals(InventoryTypeEnum.ZP.getIntValue()) ? WarehouseInventoryTypeEnum.ZP : WarehouseInventoryTypeEnum.CC);
                    itemInfo.setInventoryType(WarehouseInventoryTypeEnum.ZP);
                    itemInfo.setMateItemId(externalApiEtonbaoProperties.getRequestInfoMap().get(inWarehouseBookInfo.getOwnerCode()).getItemPrefix() + traceApplyDetailOrder.getMateSku());
                    itemInfo.setMateSku(traceApplyDetailOrder.getMateSku());
                    itemInfo.setPlanQty(traceApplyDetailOrder.getTraceQty());

                    List<WarehouseAddInStockRequest> requestFilterList = warehouseAddInStockRequests
                            .stream()
                            .filter(item -> item.getOwnerCode().equals(inWarehouseBookInfo.getOwnerCode()))
                            .collect(Collectors.toList());
                    if (CollectionUtils.isEmpty(requestFilterList)) {
                        String orderNo = logisticsTraceOrderInfo.getOrderNo() + inWarehouseBookInfo.getOwnerCode() + "_" + System.currentTimeMillis();
                        WarehouseAddInStockRequest warehouseAddInStockRequest = new WarehouseAddInStockRequest();
                        warehouseAddInStockRequest.setInStockType(this.getEtonbaoInOrderType(logisticsTraceOrderInfo));
                        warehouseAddInStockRequest.setOrderNo(orderNo);
                        warehouseAddInStockRequest.setOwnerCode(inWarehouseBookInfo.getOwnerCode());
                        warehouseAddInStockRequest.setPurchaseOrderCode(traceApplyDetailOrderList.get(0).getPurchaseOrderNo());

                        List<WarehouseInStockItemInfo> itemInfoList = new ArrayList<>();
                        itemInfoList.add(itemInfo);
                        warehouseAddInStockRequest.setItems(itemInfoList);
                        warehouseAddInStockRequests.add(warehouseAddInStockRequest);
                    } else {
                        List<WarehouseInStockItemInfo> itemFilterList = requestFilterList.get(0).getItems()
                                .stream()
                                .filter(item -> item.getMateItemId().equals(itemInfo.getMateItemId()))
                                .collect(Collectors.toList());
                        if (CollectionUtils.isEmpty(itemFilterList)) {
                            requestFilterList.get(0).getItems().add(itemInfo);
                        } else {
                            itemFilterList.get(0).setPlanQty(itemFilterList.get(0).getPlanQty() + itemInfo.getPlanQty());
                        }
                    }
                });
                for (WarehouseAddInStockRequest warehouseAddInStockRequest : warehouseAddInStockRequests) {
                    WarehouseAddInStockResponse warehouseAddInStockResponse = warehouseInter.addInStockOrder(warehouseAddInStockRequest);
                    if (warehouseAddInStockResponse.getSucceed()) {
                        countSucceed += 1;
                        // 请求记录
                        StockInOrderAddRequest stockInOrderAddRequest = new StockInOrderAddRequest();
                        stockInOrderAddRequest.setOrderNo(logisticsTraceOrderInfo.getOrderNo());
                        stockInOrderAddRequest.setPurchaseOrderNo(warehouseAddInStockRequest.getPurchaseOrderCode());
                        stockInOrderAddRequest.setOutOrderNo(warehouseAddInStockRequest.getOrderNo());
                        stockInOrderAddRequest.setOwnerCode(warehouseAddInStockRequest.getOwnerCode());
                        switch (warehouseAddInStockRequest.getInStockType()) {
                            case ETONBAO_CGRK:
                                // 采购入库
                                stockInOrderAddRequest.setInStockType(StockInOrderInStockTypeEnum.T2.getIntValue());
                                break;
                            case ETONBAO_DBRK:
                                // 调拨入库
                                stockInOrderAddRequest.setInStockType(StockInOrderInStockTypeEnum.T3.getIntValue());
                                break;
                            case ETONBAO_THRK:
                                // 退货入库
                                stockInOrderAddRequest.setInStockType(StockInOrderInStockTypeEnum.T5.getIntValue());
                                break;
                            case ETONBAO_HHRK:
                                // 换货入库
                                break;
                            case ETONBAO_SCRK:
                                // 生产入库
                                stockInOrderAddRequest.setInStockType(StockInOrderInStockTypeEnum.T1.getIntValue());
                                break;
                            case ETONBAO_CCRK:
                                // 残次品入库
                                stockInOrderAddRequest.setInStockType(StockInOrderInStockTypeEnum.T7.getIntValue());
                                break;
                            case ETONBAO_QTRK:
                                // 其他入库
                                stockInOrderAddRequest.setInStockType(StockInOrderInStockTypeEnum.T4.getIntValue());
                                break;
                            default:
                                break;
                        }
//                        stockInOrderAddRequest.setInStockStatus(warehouseAddInStockResponse.getSucceed() ? StockInOrderInStockStatusEnum.ACCEPT.getIntValue() : StockInOrderInStockStatusEnum.EXCEPTION.getIntValue());
                        stockInOrderAddRequest.setInStockStatus(StockInOrderInStockStatusEnum.ACCEPT.getIntValue());
                        List<StockInOrderAddRequest.MateItem> mateItems = new ArrayList<>();
                        warehouseAddInStockRequest.getItems().forEach(warehouseInStockItemInfo -> {
                            StockInOrderAddRequest.MateItem mateItem = new StockInOrderAddRequest.MateItem();
                            mateItem.setOrderNo(logisticsTraceOrderInfo.getOrderNo());
                            mateItem.setOrderDetailNo(warehouseInStockItemInfo.getOrderLineNo());
                            mateItem.setOutOrderNo(warehouseAddInStockRequest.getOrderNo());
                            mateItem.setMateSku(warehouseInStockItemInfo.getMateSku());
                            mateItem.setMateItemId(warehouseInStockItemInfo.getMateItemId());
                            mateItem.setPlanQty(warehouseInStockItemInfo.getPlanQty());
                            switch (warehouseInStockItemInfo.getInventoryType()) {
                                case ZP:
                                    mateItem.setInventoryType(InventoryTypeEnum.ZP.getIntValue());
                                    break;
                                case CC:
                                    mateItem.setInventoryType(InventoryTypeEnum.CC.getIntValue());
                                    break;
                                case JS:
                                    mateItem.setInventoryType(InventoryTypeEnum.JS.getIntValue());
                                    break;
                                case XS:
                                    mateItem.setInventoryType(InventoryTypeEnum.XS.getIntValue());
                                    break;
                                default:
                                    break;
                            }
                            mateItems.add(mateItem);
                        });
                        stockInOrderAddRequest.setItems(mateItems);
                        stockInOrderService.addStockInOrder(stockInOrderAddRequest);
                    } else {
                        // TODO 请求失败逻辑
                    }
                }
            }
        }
        return countSucceed;
    }

    /**
     * 外部仓库出库申请
     *
     * @param logisticsTraceOrderInfo
     */
    private int externalWarehouseOutApply(LogisticsTraceOrderInfo logisticsTraceOrderInfo) {
        int countSucceed = 0;
        List<LogisticsTraceApplyDetailOrderGetDetailResponse> traceApplyDetailOrderList = logisticsTraceApplyDetailOrderMapper
                .listLogisticsTraceApplyDetailOrderByTraceOrderNo(logisticsTraceOrderInfo.getOrderNo());
        if (CollectionUtils.isNotEmpty(traceApplyDetailOrderList)) {
            WarehouseInter warehouseInter = warehouseClient.getWarehouseService(logisticsTraceOrderInfo.getSourceWarehouseCode());
            if (warehouseInter != null) {
                List<WarehouseAddOutStockRequest> warehouseAddOutStockRequests = new ArrayList<>();
                traceApplyDetailOrderList.forEach(traceApplyDetailOrder -> {
                    SubWarehouseInfo outSubWarehouseInfo = subWarehouseMapper.getSubWarehouseByCode(traceApplyDetailOrder.getSourceSubWarehouseCode());
                    WarehouseBookInfo outWarehouseBookInfo = warehouseBookMapper.getWarehouseBookById(outSubWarehouseInfo.getWarehouseBookId());
                    WarehouseOutStockItemInfo itemInfo = new WarehouseOutStockItemInfo();
//                    itemInfo.setInventoryType(traceApplyDetailOrder.getInventoryType().equals(InventoryTypeEnum.ZP.getIntValue()) ? WarehouseInventoryTypeEnum.ZP : WarehouseInventoryTypeEnum.CC);
                    itemInfo.setInventoryType(WarehouseInventoryTypeEnum.ZP);
                    itemInfo.setMateSku(traceApplyDetailOrder.getMateSku());
                    itemInfo.setMateItemId(externalApiEtonbaoProperties.getRequestInfoMap().get(outWarehouseBookInfo.getOwnerCode()).getItemPrefix() + traceApplyDetailOrder.getMateSku());
                    itemInfo.setOrderLineNo(IdUtil.fastSimpleUUID());
                    itemInfo.setPlanQty(traceApplyDetailOrder.getTraceQty());

                    List<WarehouseAddOutStockRequest> requestFilterList = warehouseAddOutStockRequests
                            .stream()
                            .filter(item -> item.getOwnerCode().equals(outWarehouseBookInfo.getOwnerCode()))
                            .collect(Collectors.toList());
                    if (CollectionUtils.isEmpty(requestFilterList)) {
                        String orderNo = logisticsTraceOrderInfo.getOrderNo() + outWarehouseBookInfo.getOwnerCode() + "_" + System.currentTimeMillis();
                        WarehouseAddOutStockRequest warehouseAddOutStockRequest = new WarehouseAddOutStockRequest();
                        warehouseAddOutStockRequest.setOrderNo(orderNo);
                        warehouseAddOutStockRequest.setOutStockType(this.getEtonbaoOutOrderType(logisticsTraceOrderInfo));
                        warehouseAddOutStockRequest.setOwnerCode(outWarehouseBookInfo.getOwnerCode());

                        List<WarehouseOutStockItemInfo> itemInfoList = new ArrayList<>();
                        itemInfoList.add(itemInfo);
                        warehouseAddOutStockRequest.setItems(itemInfoList);
                        warehouseAddOutStockRequests.add(warehouseAddOutStockRequest);
                    } else {
                        List<WarehouseOutStockItemInfo> itemFilterList = requestFilterList.get(0).getItems()
                                .stream()
                                .filter(item -> item.getMateItemId().equals(itemInfo.getMateItemId()))
                                .collect(Collectors.toList());
                        if (CollectionUtils.isEmpty(itemFilterList)) {
                            requestFilterList.get(0).getItems().add(itemInfo);
                        } else {
                            itemFilterList.get(0).setPlanQty(itemFilterList.get(0).getPlanQty() + itemInfo.getPlanQty());
                        }
                    }
                });
                for (WarehouseAddOutStockRequest warehouseAddOutStockRequest : warehouseAddOutStockRequests) {
                    WarehouseAddOutStockResponse warehouseAddOutStockResponse = warehouseInter.addOutStockOrder(warehouseAddOutStockRequest);
                    if (warehouseAddOutStockResponse.getSucceed()) {
                        countSucceed += 1;
                        // 请求记录
                        StockOutOrderAddRequest stockOutOrderAddRequest = new StockOutOrderAddRequest();
                        stockOutOrderAddRequest.setOrderNo(logisticsTraceOrderInfo.getOrderNo());
                        stockOutOrderAddRequest.setOutOrderNo(warehouseAddOutStockRequest.getOrderNo());
                        stockOutOrderAddRequest.setOwnerCode(warehouseAddOutStockRequest.getOwnerCode());
                        switch (warehouseAddOutStockRequest.getOutStockType()) {
                            case ETONBAO_DBCK:
                                // 调拨出库
                                stockOutOrderAddRequest.setOutStockType(StockOutOrderOutStockTypeEnum.T2.getIntValue());
                                break;
                            case ETONBAO_BFCK:
                                // 补发出库
                                break;
                            case ETONBAO_CGTH:
                                // 采购退货出库单
                                stockOutOrderAddRequest.setOutStockType(StockOutOrderOutStockTypeEnum.T5.getIntValue());
                                break;
                            case ETONBAO_HHCK:
                                // 换货出库
                                break;
                            case ETONBAO_JYCK:
                                // 一般交易出库单
                                stockOutOrderAddRequest.setOutStockType(StockOutOrderOutStockTypeEnum.T3.getIntValue());
                                break;
                            case ETONBAO_PTCK:
                                // 普通出库单
                                stockOutOrderAddRequest.setOutStockType(StockOutOrderOutStockTypeEnum.T1.getIntValue());
                                break;
                            case ETONBAO_QTCK:
                                // 其他出库
                                stockOutOrderAddRequest.setOutStockType(StockOutOrderOutStockTypeEnum.T4.getIntValue());
                                break;
                            default:
                                break;
                        }
//                        stockOutOrderAddRequest.setOutStockStatus(warehouseAddOutStockResponse.getSucceed() ? StockOutOrderOutStockStatusEnum.ACCEPT.getIntValue() : StockOutOrderOutStockStatusEnum.EXCEPTION.getIntValue());
                        stockOutOrderAddRequest.setOutStockStatus(StockOutOrderOutStockStatusEnum.ACCEPT.getIntValue());
                        List<StockOutOrderAddRequest.MateItem> mateItems = new ArrayList<>();
                        warehouseAddOutStockRequest.getItems().forEach(warehouseOutStockItemInfo -> {
                            StockOutOrderAddRequest.MateItem mateItem = new StockOutOrderAddRequest.MateItem();
                            mateItem.setOrderNo(logisticsTraceOrderInfo.getOrderNo());
                            mateItem.setOrderDetailNo(warehouseOutStockItemInfo.getOrderLineNo());
                            mateItem.setOutOrderNo(warehouseAddOutStockRequest.getOrderNo());
                            mateItem.setMateSku(warehouseOutStockItemInfo.getMateSku());
                            mateItem.setMateItemId(warehouseOutStockItemInfo.getMateItemId());
                            mateItem.setPlanQty(warehouseOutStockItemInfo.getPlanQty());
                            switch (warehouseOutStockItemInfo.getInventoryType()) {
                                case ZP:
                                    mateItem.setInventoryType(InventoryTypeEnum.ZP.getIntValue());
                                    break;
                                case CC:
                                    mateItem.setInventoryType(InventoryTypeEnum.CC.getIntValue());
                                    break;
                                case JS:
                                    mateItem.setInventoryType(InventoryTypeEnum.JS.getIntValue());
                                    break;
                                case XS:
                                    mateItem.setInventoryType(InventoryTypeEnum.XS.getIntValue());
                                    break;
                                default:
                                    break;
                            }
                            mateItems.add(mateItem);
                        });
                        stockOutOrderAddRequest.setItems(mateItems);
                        stockOutOrderService.addStockOutOrder(stockOutOrderAddRequest);
                    } else {
                        // TODO 请求失败逻辑
                    }
                }
            }
        }
        return countSucceed;
    }

    private WarehouseOutStockTypeEnum getEtonbaoOutOrderType(LogisticsTraceOrderInfo logisticsTraceOrderInfo) {
        if (logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T32.getCode())) {
            return WarehouseOutStockTypeEnum.ETONBAO_DBCK;
        } else if (logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T12.getCode())) {
            return WarehouseOutStockTypeEnum.ETONBAO_CGTH;
        } else if (logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T42.getCode()) || logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T72.getCode())) {
            return WarehouseOutStockTypeEnum.ETONBAO_JYCK;
        } else {
            return WarehouseOutStockTypeEnum.ETONBAO_QTCK;
        }
    }

    private WarehouseInStockTypeEnum getEtonbaoInOrderType(LogisticsTraceOrderInfo logisticsTraceOrderInfo) {
        if (logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T11.getCode())) {
            return WarehouseInStockTypeEnum.ETONBAO_CGRK;
        } else if (logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T32.getCode())) {
            return WarehouseInStockTypeEnum.ETONBAO_DBRK;
        } else if (logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T41.getCode()) || logisticsTraceOrderInfo.getSourceOrderType().equals(SubjectEnum.T51.getCode())) {
            return WarehouseInStockTypeEnum.ETONBAO_THRK;
        } else {
            return WarehouseInStockTypeEnum.ETONBAO_QTRK;
        }
    }

    @Data
    private static class EtonbaoInStockConfirmBatch extends WarehouseEtonbaoConfirmInStockRequest.OrderLineBatch {
        private String mateSku;
    }
}
